package com.huilian.iotbox.client.consumer;
import com.alibaba.fastjson.JSONObject;
import com.huilian.iotbox.data.service.*;
import com.huilian.iotbox.data.utils.DateTimeUtils;
import com.huilian.iotbox.client.config.DelayMQConfig;
import com.huilian.iotbox.client.service.*;
import com.huilian.iotbox.data.common.RedisKey;
import com.huilian.iotbox.data.constants.DelayConstants;
import com.huilian.iotbox.data.controller.ControllerException;
import com.huilian.iotbox.data.dto.*;
import com.huilian.iotbox.data.enums.*;
import com.huilian.iotbox.data.po.*;
import com.huilian.iotbox.data.utils.SciCalUtils;
import com.huilian.iotbox.data.utils.Util;
import com.huilian.iotbox.data.vo.*;
import com.huilian.tool.weixin.service.WXSendMessageService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author 谢洛涛
 * @date 2021/1/9 16:39
 */
@Component
@Slf4j
public class RabbitReceiver {
    @Autowired
    private OrderService orderService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private OrderRefundService orderRefundService;
    @Autowired
    private WXSendMessageService wxSendMessageService;
    @Autowired
    private ErrorLogCommonService errorLogCommonService;
    @Autowired
    private DeviceFaultRecordService deviceFaultService;
    @Autowired
    private DisplayRackItemService displayRackItemService;
    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;
    @Autowired
    private PushConfigStockCommonService pushConfigStockCommonService;
    @Autowired
    private CommodityPurchaseRestrictionRecordCommonService commodityPurchaseRestrictionRecordCommonService;

    /**
     * 普通支付订单结果回调
     *
     * @param payNotice
     * @param channel
     * @param headers
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "${spring.rabbitmq.listener.iotBoxOrderNotice.queue.name}",
            durable = "${spring.rabbitmq.listener.iotBoxOrderNotice.queue.durable}"),
            exchange = @Exchange(value = "${spring.rabbitmq.listener.iotBoxOrderNotice.exchange.name}",
                    durable = "${spring.rabbitmq.listener.iotBoxOrderNotice.exchange.durable}",
                    type = "${spring.rabbitmq.listener.iotBoxOrderNotice.exchange.type}"),
            ignoreDeclarationExceptions = "${spring.rabbitmq.listener.iotBoxOrderNotice.exchange.ignoreDeclarationExceptions}",
            key = "${spring.rabbitmq.listener.iotBoxOrderNotice.key}"))
    @RabbitHandler
    public void updateOrder(@Payload PayNotice payNotice, Channel channel, @Headers Map<String, Object> headers) throws IOException {
        try {
            log.info("从队列【{}】接收到消息:{}", "updateOrder", payNotice);

            BigDecimal amount = SciCalUtils.sciCalToBD(new BigDecimal(payNotice.getTotalAmount()).divide(new BigDecimal("100")), 2);
            Order order = orderService.findByOutTradeNoAndTotalAmount(payNotice.getOutTradeNo(), amount);
            if (ObjectUtils.isEmpty(order)) {
                throw new ControllerException("订单信息有误!");
            }
            if (OrderStatusEnum.SUCCESS.getCode().equals(order.getStatus())) {
                log.info("充值订单:" + order.getOutTradeNo() + "已完成");
            } else if (OrderStatusEnum.WAIT.getCode().equals(order.getStatus())) {
                //处理支付回调业务
                orderService.handleOrder(order, payNotice);
                //插入限购记录表
                commodityPurchaseRestrictionRecordCommonService.addCommodityPurchaseRestrictionRecord(order.getId());
                // 下单成功通知
                wxSendMessageService.sendTheOrderWasPlacedMessage(order.getOutTradeNo(), order.getTotalAmount(), order.getBody(), order.getDeviceNo(), order.getUserId(), order.getId(), order.getMerchantStoreId(), new Date());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("订单回调-修改订单失败{}", e.toString());
            errorLogCommonService.writerInfoException("订单回调-修改订单失败", payNotice, e);
        } finally {
            Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
            //手工ACK
            channel.basicAck(deliveryTag, true);
        }
    }


    /**
     * 申请退款
     *
     * @param orderRefundDto
     * @param channel
     * @param headers
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "${spring.rabbitmq.listener.iotBoxOrderRefund.queue.name}",
            durable = "${spring.rabbitmq.listener.iotBoxOrderRefund.queue.durable}"),
            exchange = @Exchange(value = "${spring.rabbitmq.listener.iotBoxOrderRefund.exchange.name}",
                    durable = "${spring.rabbitmq.listener.iotBoxOrderRefund.exchange.durable}",
                    type = "${spring.rabbitmq.listener.iotBoxOrderRefund.exchange.type}"),
            ignoreDeclarationExceptions = "${spring.rabbitmq.listener.iotBoxOrderRefund.exchange.ignoreDeclarationExceptions}",
            key = "${spring.rabbitmq.listener.iotBoxOrderRefund.key}"))
    @RabbitHandler
    public void insertOrderRefund(@Payload OrderRefundDto orderRefundDto, Channel channel, @Headers Map<String, Object> headers) throws IOException {
        try {
            log.info("从队列【{}】接收到申请退款消息:{}", "outRefundNo", orderRefundDto.getOutRefundNo());
            orderRefundService.insertRefundOrder(orderRefundDto);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("退款异常{}", e.toString());
            errorLogCommonService.writerInfoException("退款异常", orderRefundDto, e);
        } finally {
            Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
            //手工ACK
            channel.basicAck(deliveryTag, true);
        }
    }

    /**
     * 普通订单退款
     *
     * @param orderRefundDto
     * @param channel
     * @param headers
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.queue.name}",
            durable = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.queue.durable}"),
            exchange = @Exchange(value = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.exchange.name}",
                    durable = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.exchange.durable}",
                    type = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.exchange.type}"),
            ignoreDeclarationExceptions = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.exchange.ignoreDeclarationExceptions}",
            key = "${spring.rabbitmq.listener.iotBoxQueryOrderRefund.key}"))
    @RabbitHandler
    public void updateOrderRefund(@Payload OrderRefundDto orderRefundDto, Channel channel, @Headers Map<String, Object> headers) throws IOException {
        try {
            log.info("从队列【{}】接收到退款查询消息:{}", "outRefundNo", orderRefundDto.getOutRefundNo());
            orderRefundService.updateRefundOrder(orderRefundDto);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("退款异常{}", e.toString());
            errorLogCommonService.writerInfoException("退款异常", orderRefundDto, e);
        } finally {
            Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
            //手工ACK
            channel.basicAck(deliveryTag, true);
        }
    }


    /**
     * 监听接收到售货机延迟队列消息
     *
     * @param json
     * @param channel
     * @param headers
     * @throws IOException
     */
    @RabbitListener(queues = DelayMQConfig.IOT_BOX_CLIENT_vending_machine_LAZY_QUEUE)
    @RabbitHandler
    public void onVendingMachineReplayLazyQueues(@Payload String json, Channel channel, @Headers Map<String, Object> headers) throws IOException {
        try {
            log.info("接收到售货机延时检测队列：{}", json);
            final JSONObject resultObject = JSONObject.parseObject(json);
            final String step = resultObject.getString("step");
            if (DelayConstants.VENDING_MACHINE.equals(step)) {
                log.info("接收到售货机延时检测队列：{}", json);
                Order order = JSONObject.parseObject(resultObject.getString("json"), Order.class);
                Order newOrder = orderService.findById(order.getId());
                if (newOrder.getShipmentStatus().equals(ShipmentStatusEnum.SUCCESS.getCode())) {
                    log.info("出货成功");
                    newOrder.setShoutStatus(OrderShoutStatusEnum.SUCCESS.getCode());
                    orderService.updateOrderShoutStatus(newOrder);

                    List<OrderItem> items = orderItemService.getOrderItemById(newOrder.getId());
                    for (OrderItem oi : items) {
                        DisplayRackItemDto displayRackItemDto = new DisplayRackItemDto();
                        displayRackItemDto.setDeviceId(oi.getDeviceId());
                        displayRackItemDto.setHierarchy(oi.getHierarchy());
                        displayRackItemDto.setFreightLane(oi.getFreightLane());
                        displayRackItemDto.setIsDelete(false);
                        //货道库存
                        DisplayRackItemVo dri = displayRackItemService.findDisplayRackItemByDeviceId(displayRackItemDto);

                        StringBuilder stringBuilder = new StringBuilder();
                        if (StringUtils.isEmpty(dri.getAlias())) {
                            stringBuilder.append(dri.getHierarchy());
                            stringBuilder.append("-");
                            stringBuilder.append(dri.getFreightLane());
                        } else {
                            stringBuilder.append(dri.getAlias());
                        }

                        //设备是否绑定了通知
                        PushConfigStockVo pushConfigStockVo = pushConfigStockCommonService.genOnePushConfigStockByDeviceIdAndUserId(oi.getDeviceId(),newOrder.getUserId());
                        if(!StringUtils.isEmpty(pushConfigStockVo)){

                            if (!StringUtils.isEmpty(dri)) {
                                //整机库存
                                DisplayRackItemVo displayRackItemVoDevice = displayRackItemService.findDisplayRackItemByDeviceIdAndCommodityId(displayRackItemDto);

                                //商品库存
                                displayRackItemDto.setCommodityId(dri.getCommodityId());
                                DisplayRackItemVo displayRackItemVoCommodity = displayRackItemService.findDisplayRackItemByDeviceIdAndCommodityId(displayRackItemDto);

                                Integer totalStock = -1;
                                String goodName = "";
                                if(PushConfigStockTypeEnum.COMPLETE_MACHINE_TYPE.getCode().compareTo(pushConfigStockVo.getType()) == 0){
                                    if(!StringUtils.isEmpty(displayRackItemVoCommodity)){
                                        totalStock = displayRackItemVoCommodity.getTotalStock();
                                    }
                                    stringBuilder.setLength(0);
                                    stringBuilder.append(" ");
                                    //截取商品
                                    goodName = this.substringCommodityName("...分类库存剩%s个","分类库存剩%s个",totalStock,dri.getCommodityName());
                                    log.info("当前商品库存：{}", totalStock);
                                    log.info("整机分类商品拼接后的名称：{}",goodName);
                                } else if (PushConfigStockTypeEnum.COMPLETE_MACHINE.getCode().compareTo(pushConfigStockVo.getType()) == 0) {
                                    if(!StringUtils.isEmpty(displayRackItemVoDevice)){
                                        totalStock = displayRackItemVoDevice.getTotalStock();
                                    }
                                    stringBuilder.setLength(0);
                                    stringBuilder.append(" ");
                                    //截取商品
                                    goodName = this.substringCommodityName("...整机库存剩%s个","整机库存剩%s个",totalStock,dri.getCommodityName());
                                    log.info("当前整机库存：{}", totalStock);
                                    log.info("整机商品拼接后的名称：{}",goodName);
                                } else if (PushConfigStockTypeEnum.FREIGHT_ROAD.getCode().compareTo(pushConfigStockVo.getType()) == 0) {
                                    totalStock = dri.getStock();
                                    goodName = dri.getCommodityName();
                                    log.info("当前货道库存：{}", totalStock);
                                }
                                if(totalStock <= pushConfigStockVo.getWarnStock() && totalStock >= 0){
                                    // 缺货通知
                                    log.info("进入缺货通知：{}", totalStock);
                                    wxSendMessageService.sendVendingMachineOutOfStockMessage(newOrder.getDeviceNo(), oi.getDeviceId(), stringBuilder.toString(), totalStock, dri.getStoreName(), goodName, newOrder.getUserId(), order.getId(), newOrder.getMerchantStoreId());
                                }else {
                                    log.info("当前库存没有达到触发预警：{}", totalStock);
                                }
                                log.info("触发预警库存：{}", pushConfigStockVo.getWarnStock());
                            }

                        }else{
                            log.info("该设备未绑定通知：{}",oi.getDeviceId());
                        }
                        // 出货成功通知
                        wxSendMessageService.sendVendingShipmentSuccessMessage(dri.getCommodityName(), newOrder.getDeviceNo(), stringBuilder.toString(), dri.getStoreName(), newOrder.getUserId(), newOrder.getMerchantStoreId());
                    }
                } else if (newOrder.getShipmentStatus().equals(ShipmentStatusEnum.WAIT.getCode())) {
                    log.info("延迟查询待出货状态，退款,加库存,通知");
                    DeviceVo deviceVo = deviceService.findOneDeviceByDeviceNo(order.getDeviceNo());
                    List<OrderItem> list = orderItemService.getOrderItemById(newOrder.getId());
                    List<DisplayRackItemVo> displayRackItemVoList = new ArrayList<>();

                    for (OrderItem orderItem : list) {
                        //退回库存
                        DisplayRackItemDto displayRackItemDto = new DisplayRackItemDto();
                        displayRackItemDto.setDeviceId(orderItem.getDeviceId());
                        displayRackItemDto.setHierarchy(orderItem.getHierarchy());
                        displayRackItemDto.setFreightLane(orderItem.getFreightLane());
                        displayRackItemDto.setIsDelete(false);
                        DisplayRackItemVo displayRackItemVo = displayRackItemService.findDisplayRackItemByDeviceId(displayRackItemDto);
                        //货道库存没有补满就退回库存
                        if (displayRackItemVo.getStock() < displayRackItemVo.getCapacity()) {
                            log.info("退回库存");
                            int minStock = displayRackItemVo.getCapacity() - displayRackItemVo.getStock();
                            if (minStock >= orderItem.getNum()) {
                                displayRackItemDto.setStock(displayRackItemVo.getStock() + orderItem.getNum());
                            } else {
                                displayRackItemDto.setStock(displayRackItemVo.getStock() + minStock);
                            }
                            displayRackItemDto.setId(displayRackItemVo.getId());
                            displayRackItemDto.setVersion(displayRackItemVo.getVersion());
                            displayRackItemService.updateStockById(displayRackItemDto);
                        } else {
                            log.info("货道库存已补满不执行退回库存,订单号(OrderNo)：{}，设备号(DeviceNo)：{}，货道号(Alias)：{}",
                                    order.getOrderNo(), deviceVo.getDeviceNo(), displayRackItemVo.getAlias());
                        }

                        //修改orderItem表状态
                        OrderItem orderItemUpdate = new OrderItem();
                        if (orderItem.getShipmentStatus().equals(ShipmentStatusEnum.WAIT.getCode())) {
                            orderItemUpdate.setShipmentStatus(ShipmentStatusEnum.FAIL.getCode());
                            if (!ObjectUtils.isEmpty(deviceVo.getAutoRefund()) && deviceVo.getAutoRefund()) {
                                orderItemUpdate.setStatus(OrderStatusEnum.REFUNDING.getCode());
                            }
                            orderItemUpdate.setOrderId(orderItem.getOrderId());
                            orderItemService.update(orderItemUpdate);
                        }

                        displayRackItemVo.setShipmentStatus(ObjectUtils.isEmpty(orderItemUpdate.getShipmentStatus())
                                ? orderItem.getShipmentStatus() : orderItemUpdate.getShipmentStatus());
                        displayRackItemVoList.add(displayRackItemVo);
                    }
                    //有失败订单就发送通知
                    for (DisplayRackItemVo dri : displayRackItemVoList) {
                        if (ShipmentStatusEnum.FAIL.getCode().equals(dri.getShipmentStatus()) || ShipmentStatusEnum.PART_FAIL.getCode().equals(dri.getShipmentStatus())) {
                            wxSendMessageService.sendVendingShipmentFailMessage(newOrder.getDeviceNo(), newOrder.getOutTradeNo(), dri.getCommodityName(), dri.getDeviceId(), newOrder.getUserId(), order.getId(), newOrder.getMerchantStoreId());
                        }
                    }
                    if (!ObjectUtils.isEmpty(deviceVo.getAutoRefund()) && deviceVo.getAutoRefund()) {
                        OrderRefundDto orderRefundDto = new OrderRefundDto();
                        orderRefundDto.setMerchantId(newOrder.getMerchantId());
                        orderRefundDto.setRefundAmount(newOrder.getTotalAmount());
                        orderRefundDto.setOutTradeNo(newOrder.getOutTradeNo());
                        orderRefundDto.setRefundReason("售货机出货失败");
                        orderRefundDto.setOutRefundNo("refund" + Util.getOutTradeNo());
                        orderService.orderRefund(orderRefundDto);
                        //退款后修改订单状态
                        newOrder.setRefundType(1);
                        newOrder.setRefundTotalAmount(newOrder.getTotalAmount());
                    }
                    newOrder.setShipmentStatus(ShipmentStatusEnum.FAIL.getCode());
                    orderService.updateOrder(newOrder);

                    newOrder.setShoutStatus(OrderShoutStatusEnum.SUCCESS.getCode());
                    orderService.updateOrderShoutStatus(newOrder);

                    try {
                        if(!StringUtils.isEmpty(deviceVo.getPayLimitTime()) && deviceVo.getPayLimitTime()>0) {
                            String memberPayDetailKey = String.format(RedisKey.LIMITATION_SIMULTANEOUS_PAYMENT_VENDING_MACHINE, deviceVo.getId());
                            redisCacheTemplate.opsForValue().set(memberPayDetailKey, 2, 30, TimeUnit.DAYS);
                            DeviceFaultRecord deviceFaultRecord = DeviceFaultRecord.builder().
                                        deviceId(deviceVo.getId()).
                                        userId(deviceVo.getUserId()).
                                        merchantStoreId(deviceVo.getMerchantStoreId()).
                                        fault(1).
                                        name("出货失败").
                                        build();
                            deviceFaultService.insert(deviceFaultRecord);
                            wxSendMessageService.sendFaultMessage(deviceVo.getDeviceNo(), deviceVo.getStoreName(), DateTimeUtils.currTime(), null, deviceFaultRecord.getName(), deviceVo.getUserId(), deviceFaultRecord.getMerchantStoreId());
                        }
                    }catch (Exception e){
                        log.info("插入售货机故障失败");
                    }



                } else if (newOrder.getShipmentStatus().equals(ShipmentStatusEnum.FAIL.getCode())
                        || newOrder.getShipmentStatus().equals(ShipmentStatusEnum.PART_FAIL.getCode())) {
                    log.info("出货失败，退款，通知");
                    DeviceVo deviceVo = deviceService.findOneDeviceByDeviceNo(order.getDeviceNo());
                    List<OrderItem> list = orderItemService.getOrderItemById(newOrder.getId());
                    List<DisplayRackItemVo> displayRackItemVoList = new ArrayList<>();

                    for (OrderItem orderItem : list) {
                        //查询出货失败货道
                        DisplayRackItemDto displayRackItemDto = new DisplayRackItemDto();
                        displayRackItemDto.setDeviceId(orderItem.getDeviceId());
                        displayRackItemDto.setHierarchy(orderItem.getHierarchy());
                        displayRackItemDto.setFreightLane(orderItem.getFreightLane());
                        displayRackItemDto.setIsDelete(false);
                        DisplayRackItemVo displayRackItemVo = displayRackItemService.findDisplayRackItemByDeviceId(displayRackItemDto);

                        displayRackItemVo.setShipmentStatus(orderItem.getShipmentStatus());
                        displayRackItemVoList.add(displayRackItemVo);
                    }
                    //有失败订单就发送通知
                    for (DisplayRackItemVo dri : displayRackItemVoList) {
                        if (ShipmentStatusEnum.SUCCESS.getCode().equals(dri.getShipmentStatus())) {
                            StringBuilder stringBuilder = new StringBuilder();
                            if (StringUtils.isEmpty(dri.getAlias())) {
                                stringBuilder.append(dri.getHierarchy());
                                stringBuilder.append("-");
                                stringBuilder.append(dri.getFreightLane());
                            } else {
                                stringBuilder.append(dri.getAlias());
                            }
                            wxSendMessageService.sendVendingShipmentSuccessMessage(dri.getCommodityName(), newOrder.getDeviceNo(), stringBuilder.toString(), dri.getStoreName(), newOrder.getUserId(), newOrder.getMerchantStoreId());
                        }
                        if (ShipmentStatusEnum.FAIL.getCode().equals(dri.getShipmentStatus()) || ShipmentStatusEnum.PART_FAIL.getCode().equals(dri.getShipmentStatus())) {
                            wxSendMessageService.sendVendingShipmentFailMessage(newOrder.getDeviceNo(), newOrder.getOutTradeNo(), dri.getCommodityName(), dri.getDeviceId(), newOrder.getUserId(), order.getId(), newOrder.getMerchantStoreId());
                        }
                    }
                    //退款
                    if (newOrder.getShipmentStatus().equals(ShipmentStatusEnum.FAIL.getCode())
                            && !ObjectUtils.isEmpty(deviceVo.getAutoRefund()) && deviceVo.getAutoRefund()) {
                        OrderRefundDto orderRefundDto = new OrderRefundDto();
                        orderRefundDto.setMerchantId(newOrder.getMerchantId());
                        orderRefundDto.setRefundAmount(newOrder.getTotalAmount());
                        orderRefundDto.setOutTradeNo(newOrder.getOutTradeNo());
                        orderRefundDto.setRefundReason("售货机出货失败");
                        orderRefundDto.setOutRefundNo("refund" + Util.getOutTradeNo());
                        orderService.orderRefund(orderRefundDto);
                    }
                    newOrder.setShoutStatus(OrderShoutStatusEnum.SUCCESS.getCode());
                    orderService.updateOrderShoutStatus(newOrder);

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("售货机延时检测队列处理异常：{}", e.toString());
            errorLogCommonService.writerInfoException("售货机延时检测队列处理异常", json, e);
        } finally {
            Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
            //手工ACK
            channel.basicAck(deliveryTag, true);
        }
    }

    private String substringCommodityName(String s, String s1, Integer totalStock,String commodityName) {
        String goodName;
        if(commodityName.length() > 7){
            String name = String.format(s, totalStock);
            String substr7 = commodityName.substring(0, 7);
            goodName =  substr7 + name;
        }else {
            goodName = commodityName + String.format(s1, totalStock);
        }
        return goodName;
    }

}
